﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ChessPiece.cs" company="Clounlene Inc.">
//   (c) 2010-2020.  All Rights Reserved.
// </copyright>
//  <summary>
//   The chess piece.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Shatranj
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Controls;

    /// <summary>
    ///     The chess piece.
    /// </summary>
    public abstract class ChessPiece
    {
        #region Static Fields

        /// <summary>
        ///     The all move paths.
        /// </summary>
        private static readonly IDictionary<string, IDictionary<string, ICollection<MovePath>>> AllMovePaths;

        /// <summary>
        ///     The sync root.
        /// </summary>
        private static readonly object SyncRoot = new object();

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///     Initializes static members of the <see cref="ChessPiece" /> class.
        /// </summary>
        static ChessPiece()
        {
            AllMovePaths = new Dictionary<string, IDictionary<string, ICollection<MovePath>>>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChessPiece"/> class.
        /// </summary>
        /// <param name="currentSquare">
        /// The current square.
        /// </param>
        protected ChessPiece(BoardSquare currentSquare)
            : this(currentSquare, Army.White)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChessPiece"/> class.
        /// </summary>
        /// <param name="currentSquare">
        /// The current square.
        /// </param>
        /// <param name="force">
        /// The force.
        /// </param>
        protected ChessPiece(BoardSquare currentSquare, Army force)
        {
            this.CurrentLocation = currentSquare;
            this.Force = force;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChessPiece"/> class.
        /// </summary>
        /// <param name="currentSquare">
        /// The current square.
        /// </param>
        /// <param name="force">
        /// The force.
        /// </param>
        /// <param name="isForceAffinitive">
        /// The is force affinitive.
        /// </param>
        protected ChessPiece(BoardSquare currentSquare, Army force, bool isForceAffinitive)
            : this(currentSquare, force)
        {
            this.IsForceAffinitive = isForceAffinitive;
        }

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets or sets the algebraic identity.
        /// </summary>
        public char AlgebraicIdentity { get; set; }

        /// <summary>
        ///     Gets or sets the alt char.
        /// </summary>
        public char AltChar { get; set; }

        /// <summary>
        ///     Gets or sets a value indicating whether can capture across.
        /// </summary>
        public bool CanCaptureAcross { get; protected set; }

        /// <summary>
        ///     Gets or sets a value indicating whether can jump over.
        /// </summary>
        public bool CanJumpOver { get; protected set; }

        /// <summary>
        ///     Gets or sets the char notation as number.
        /// </summary>
        public int CharNotationAsNumber { get; set; }

        /// <summary>
        ///     Gets or sets the current location.
        /// </summary>
        public BoardSquare CurrentLocation { get; set; }

        /// <summary>
        ///     Gets or sets a value indicating whether it's Enpassant.
        /// </summary>
        public bool Enpassant { get; protected set; }

        /// <summary>
        ///     Gets or sets the figurine notation.
        /// </summary>
        public string FigurineNotation { get; set; }

        /// <summary>
        ///     Gets or sets the force.
        /// </summary>
        public Army Force { get; set; }

        /// <summary>
        ///     Gets or sets the identity.
        /// </summary>
        public Piece Identity { get; set; }

        /// <summary>
        ///     Gets or sets the image.
        /// </summary>
        public Image Image { get; set; }

        /// <summary>
        ///     Gets or sets the initial max step.
        /// </summary>
        public int InitialMaxStep { get; protected set; }

        /// <summary>
        ///     Gets or sets a value indicating whether initial max step allowed.
        /// </summary>
        public bool InitialMaxStepAllowed { get; protected set; }

        /// <summary>
        ///     Gets or sets a value indicating whether is force affinitive.
        /// </summary>
        public bool IsForceAffinitive { get; protected set; }

        /// <summary>
        ///     Gets a value indicating whether is power.
        /// </summary>
        public virtual bool IsPower
        {
            get
            {
                return this.Identity != Piece.Pawn;
            }
        }

        /// <summary>
        ///     Gets or sets a value indicating whether is promotable.
        /// </summary>
        public bool IsPromotable { get; protected set; }

        /// <summary>
        ///     Gets or sets the legal moves.
        /// </summary>
        public ICollection<BoardSquare> LegalMoves { get; protected set; }

        /// <summary>
        ///     Gets the move paths.
        /// </summary>
        public IDictionary<string, ICollection<MovePath>> MovePaths
        {
            get
            {
                lock (SyncRoot)
                {
                    var id = this.Identity + (this.IsForceAffinitive ? this.Force.ToString() : string.Empty);
                    return !AllMovePaths.ContainsKey(id) ? null : AllMovePaths[id];
                }
            }
        }

        /// <summary>
        ///     Gets or sets the original location.
        /// </summary>
        public BoardSquare OriginalLocation { get; set; }

        /// <summary>
        ///     Gets or sets the times moved.
        /// </summary>
        public int TimesMoved { get; set; }

        #endregion

        #region Public Methods and Operators

        /// <summary>
        ///     The compute legal moves.
        /// </summary>
        public virtual void ComputeLegalMoves()
        {
            this.LegalMoves = MoveCalculator.Calculate(this, this.CurrentLocation, MoveOptions.AllApplicable);
        }

        /// <summary>
        /// If the sqaure
        /// </summary>
        /// <param name="square">
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public bool IsLegal(BoardSquare square)
        {
            return this.LegalMoves.Where(x => x.AlgebraicIdentity == square.AlgebraicIdentity).Count() == 1;
        }

        /// <summary>
        /// The move.
        /// </summary>
        /// <param name="newLocation">
        /// The new location.
        /// </param>
        /// <param name="options">
        /// The options.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        public abstract bool Move(BoardSquare newLocation, MoveOptions options);

        /// <summary>
        ///     The move.
        /// </summary>
        /// <returns>
        ///     The <see cref="bool" />.
        /// </returns>
        public abstract bool Move();

        #endregion

        #region Methods

        /// <summary>
        /// The set move paths.
        /// </summary>
        /// <param name="identity">
        /// The identity.
        /// </param>
        /// <param name="movePathGroups">
        /// The move path groups.
        /// </param>
        protected void SetMovePaths(Piece identity, IDictionary<string, ICollection<MovePath>> movePathGroups)
        {
            var id = identity + (this.IsForceAffinitive ? this.Force.ToString() : string.Empty);
            lock (SyncRoot) AllMovePaths[id] = movePathGroups;
        }

        #endregion
    }

    /// <summary>
    ///     The move direction.
    /// </summary>
    public enum MoveDirection
    {
        /// <summary>
        ///     The north.
        /// </summary>
        North, 

        /// <summary>
        ///     The northeast.
        /// </summary>
        Northeast, 

        /// <summary>
        ///     The east.
        /// </summary>
        East, 

        /// <summary>
        ///     The southeast.
        /// </summary>
        Southeast, 

        /// <summary>
        ///     The south.
        /// </summary>
        South, 

        /// <summary>
        ///     The southwest.
        /// </summary>
        Southwest, 

        /// <summary>
        ///     The west.
        /// </summary>
        West, 

        /// <summary>
        ///     The northwest.
        /// </summary>
        Northwest
    }

    /// <summary>
    ///     The move group.
    /// </summary>
    public class MoveGroup
    {
        #region Public Properties

        /// <summary>
        ///     Gets or sets the path.
        /// </summary>
        public ICollection<MovePath> Path { get; set; }

        #endregion
    }

    /// <summary>
    ///     The move path.
    /// </summary>
    public class MovePath
    {
        #region Public Properties

        /// <summary>
        ///     Gets or sets the direction.
        /// </summary>
        public MoveDirection Direction { get; set; }

        /// <summary>
        ///     Gets or sets the group id.
        /// </summary>
        public string GroupId { get; set; }

        /// <summary>
        ///     Gets or sets a value indicating whether is grouped.
        /// </summary>
        public bool IsGrouped { get; set; }

        /// <summary>
        ///     Gets or sets the max step.
        /// </summary>
        public int MaxStep { get; set; }

        /// <summary>
        ///     Gets or sets the min step.
        /// </summary>
        public int MinStep { get; set; }

        /// <summary>
        ///     Gets or sets the ordinal.
        /// </summary>
        public int Ordinal { get; set; }

        #endregion

        // public PieceSpeciality Speciality { get; set; }
    }
}